# SSL修复 - 必须在其他网络库导入之前执行
import ssl_fix

import sys
import os
import json
import pymysql
import urllib.request
import ssl
from PyQt5.QtCore import QThread, pyqtSignal
from config import config
from db_connection import DatabaseConnection

class VideoDownloader(QThread):
    """视频下载器类，负责从数据库读取商品信息并下载相关视频"""
    
    progress_updated = pyqtSignal(str)
    download_finished = pyqtSignal()
    error_occurred = pyqtSignal(str)
    
    def __init__(self, db_config, download_path, thread_count=3, ignore_commission_filter=False):
        super().__init__()
        self.db_config = db_config
        self.download_path = download_path
        self.thread_count = thread_count
        self.ignore_commission_filter = ignore_commission_filter
        self.running = False
        # 添加累计下载视频数目统计
        self.total_downloaded_videos = 0
        # 添加调试日志控制标志
        self.debug_enabled = False
        
    def stop(self):
        """停止视频下载任务"""
        self.running = False
        
    def set_debug_enabled(self, enabled):
        """设置调试日志是否启用"""
        self.debug_enabled = enabled
        
    def debug_log(self, message):
        """调试日志输出"""
        if self.debug_enabled:
            self.progress_updated.emit(message)
            
    def error_log(self, message):
        """错误日志输出（红色）"""
        # 发送带有错误前缀的消息，前端会将其显示为红色
        self.progress_updated.emit(f"ERROR: {message}")
            
    def run(self):
        try:
            self.running = True
            self.progress_updated.emit("开始连接数据库...")
            
            # 连接数据库
            connection = DatabaseConnection.create_connection(self.db_config)
            
            try:
                while self.running:
                    # 获取指定数量的数据，每个线程处理一个商品
                    with connection.cursor(pymysql.cursors.DictCursor) as cursor:
                        select_sql = "SELECT * FROM ks_goods_rank WHERE deleted = 0 LIMIT %s"
                        cursor.execute(select_sql, (self.thread_count,))
                        records = cursor.fetchall()
                        
                        if not records:
                            self.progress_updated.emit("没有更多数据需要处理")
                            break
                            
                        self.progress_updated.emit(f"获取到 {len(records)} 条数据进行处理")
                        
                        # 逐个处理每个商品，确保一个线程只处理一个商品的所有视频
                        for record in records:
                            if not self.running:
                                break
                                
                            try:
                                # 更新记录为处理中状态
                                with connection.cursor() as update_cursor:
                                    update_sql = "UPDATE ks_goods_rank SET deleted = 2 WHERE id = %s"
                                    update_cursor.execute(update_sql, (record['id'],))
                                    connection.commit()
                                
                                # 解析视频列表
                                video_list = json.loads(record['videoList']) if record['videoList'] else []
                                
                                # 只有在调试模式下才输出详细日志
                                self.debug_log(f"处理商品 {record['relItemId']} 的视频列表: {video_list}")
                                
                                if not video_list:
                                    # 没有视频，打印警告日志并直接标记为已完成
                                    self.progress_updated.emit(f"警告: 商品 {record['relItemId']} 的视频列表为空，跳过下载")
                                    with connection.cursor() as update_cursor:
                                        update_sql = "UPDATE ks_goods_rank SET deleted = 1 WHERE id = %s"
                                        update_cursor.execute(update_sql, (record['id'],))
                                        connection.commit()
                                    continue
                                
                                # 创建目录结构
                                category_name = record['categoryName']
                                commission_rate = float(record['commissionRate']) if record['commissionRate'] else 0
                                
                                # 确定佣金率范围文件夹
                                rate_folder = self.get_rate_folder(commission_rate)
                                if not rate_folder:
                                    # 如果开启了不过滤佣金，则使用佣金率本身作为文件夹名称
                                    if self.ignore_commission_filter:
                                        # 格式化佣金率，如果是整数则转为整数显示
                                        if commission_rate.is_integer():
                                            rate_folder = str(int(commission_rate))
                                        else:
                                            rate_folder = str(commission_rate)
                                    else:
                                        # 不在指定范围内，跳过
                                        with connection.cursor() as update_cursor:
                                            update_sql = "UPDATE ks_goods_rank SET deleted = 1 WHERE id = %s"
                                            update_cursor.execute(update_sql, (record['id'],))
                                            connection.commit()
                                        self.progress_updated.emit(f"商品 {record['relItemId']} 的佣金率 {commission_rate} 不在指定范围内，跳过")
                                        self.debug_log(f"商品 {record['relItemId']} 的佣金率 {commission_rate} 不在指定范围内，跳过")
                                        continue
                                
                                # 创建完整路径（根据是否开启子类目）
                                enable_sub = False
                                try:
                                    enable_sub = config.get("download.enable_subcategory", False)
                                    # 兼容可能的嵌套结构
                                    if isinstance(enable_sub, dict):
                                        enable_sub = enable_sub.get("value", False)
                                except Exception:
                                    enable_sub = False

                                # 目录名清洗
                                safe_category = self.safe_folder_name(category_name or "未分类")
                                sub_category_name = record.get('subCategoryName') or ""
                                safe_sub_category = self.safe_folder_name(sub_category_name) if sub_category_name else ""

                                if enable_sub and safe_sub_category:
                                    full_path = os.path.join(self.download_path, safe_category, safe_sub_category, rate_folder)
                                else:
                                    full_path = os.path.join(self.download_path, safe_category, rate_folder)
                                os.makedirs(full_path, exist_ok=True)
                                
                                # 下载视频 - 一个商品的所有视频在一个线程内顺序下载
                                downloaded_count = 0
                                for i, video_url in enumerate(video_list[:5]):  # 最多下载前5个视频
                                    if not self.running:
                                        break
                                        
                                    # 生成符合要求的文件名：商品id{relItemId}视频标题{itemTitle}{N}
                                    # N从01开始
                                    n = f"{i+1:02d}"  # 格式化为两位数，从01开始
                                    item_title = record['itemTitle'] if record['itemTitle'] else "未知标题"
                                    # 清理文件名中的非法字符
                                    cleaned_title = self.clean_filename(item_title)
                                    filename = f"商品id{record['relItemId']}视频标题{cleaned_title}{n}.mp4"
                                    file_path = os.path.join(full_path, filename)
                                    
                                    # 只有在调试模式下才输出详细日志
                                    self.debug_log(f"下载视频: {video_url} -> {filename}")
                                    
                                    # 实际下载视频文件
                                    # 创建不验证SSL证书的上下文（仅用于测试环境）
                                    context = ssl.create_default_context()
                                    context.check_hostname = False
                                    context.verify_mode = ssl.CERT_NONE
                                    
                                    # 使用urllib下载视频数据（流式下载，避免内存不足）
                                    headers = {
                                        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
                                    }
                                    req = urllib.request.Request(video_url, headers=headers)
                                    
                                    # 添加重试机制
                                    max_retries = 3
                                    for retry in range(max_retries):
                                        try:
                                            with urllib.request.urlopen(req, context=context, timeout=60) as response:
                                                # 流式下载，分块写入文件
                                                chunk_size = 8192  # 8KB块大小
                                                with open(file_path, 'wb') as f:
                                                    while True:
                                                        chunk = response.read(chunk_size)
                                                        if not chunk:
                                                            break
                                                        f.write(chunk)
                                            break  # 下载成功，跳出重试循环
                                        except urllib.error.HTTPError as http_err:
                                            if http_err.code == 404:
                                                # 404错误特殊处理，记录日志但不重试
                                                self.progress_updated.emit(f"警告: 视频文件不存在 (404 Not Found): {video_url}")
                                                self.debug_log(f"处理商品 {record['relItemId']} 时遇到404错误，视频URL: {video_url}")
                                                break  # 直接跳出，不再重试
                                            elif retry < max_retries - 1:
                                                self.debug_log(f"下载视频第{retry + 1}次失败，准备重试: HTTP {http_err.code} - {http_err.reason}")
                                                # 等待一段时间再重试
                                                self.sleep(2 ** retry)  # 指数退避
                                            else:
                                                raise http_err  # 最后一次重试失败，重新抛出异常
                                        except Exception as e:
                                            if retry < max_retries - 1:
                                                self.debug_log(f"下载视频第{retry + 1}次失败，准备重试: {e}")
                                                # 等待一段时间再重试
                                                self.sleep(2 ** retry)  # 指数退避
                                            else:
                                                raise e  # 最后一次重试失败，重新抛出异常
                                    
                                    downloaded_count += 1
                                    self.total_downloaded_videos += 1
                                    # 修改消息格式以匹配ProgressDialog的更新逻辑
                                    self.progress_updated.emit(f"下载视频: {filename} (累计下载: {self.total_downloaded_videos} 个视频)")
                                    
                                # 商品所有视频下载完成后，标记为已完成
                                try:
                                    with connection.cursor() as update_cursor:
                                        update_sql = "UPDATE ks_goods_rank SET deleted = 1 WHERE id = %s"
                                        update_cursor.execute(update_sql, (record['id'],))
                                        connection.commit()
                                except Exception as e:
                                    self.error_log(f"更新数据库记录失败: {str(e)}")
                                    
                                self.progress_updated.emit(f"商品 {record['relItemId']} 处理完成，下载了 {downloaded_count} 个视频 (累计下载: {self.total_downloaded_videos} 个视频)")
                                # 只有在调试模式下才输出详细日志
                                self.debug_log(f"商品 {record['relItemId']} 处理完成，下载了 {downloaded_count} 个视频，佣金率: {record['commissionRate']}, 类目: {record['categoryName']}")
                                
                            except Exception as e:
                                # 出错时将状态更新为2（异常状态）
                                with connection.cursor() as update_cursor:
                                    update_sql = "UPDATE ks_goods_rank SET deleted = 2 WHERE id = %s"
                                    update_cursor.execute(update_sql, (record['id'],))
                                    connection.commit()
                                # 对HTTPError 404进行特殊处理
                                if "HTTPError 404" in str(e) or "404 Not Found" in str(e):
                                    self.progress_updated.emit(f"警告: 处理商品 {record['relItemId']} 时遇到404错误，视频资源可能已失效")
                                else:
                                    self.error_occurred.emit(f"处理商品 {record['relItemId']} 时出错: {str(e)}")
                                continue
                                
                        # 添加延迟，避免请求过于频繁
                        if self.running and records:
                            delay = config.get("download.video_request_delay", 15)
                            # 确保delay是整数类型
                            delay_int = 5
                            if delay is not None:
                                try:
                                    if isinstance(delay, dict):
                                        delay_value = delay.get("value", 5)
                                        delay_int = int(delay_value) if delay_value is not None else 5
                                    else:
                                        delay_int = int(delay)
                                except (ValueError, TypeError):
                                    delay_int = 5
                            self.progress_updated.emit(f"暂停{delay_int}秒避免请求过于频繁... (累计下载: {self.total_downloaded_videos} 个视频)")
                            self.sleep(delay_int)  # 暂停指定秒数
                            
            finally:
                connection.close()
                
            self.progress_updated.emit(f"视频下载任务完成! 累计下载了 {self.total_downloaded_videos} 个视频")
            self.download_finished.emit()
            
        except Exception as e:
            self.error_occurred.emit(f"视频下载过程中出错: {str(e)}")
        finally:
            self.running = False

    def get_rate_folder(self, commission_rate):
        """根据佣金率确定文件夹名称"""
        rate_ranges = [
            (15, 19), (20, 24), (25, 29), (30, 34),
            (35, 39), (40, 44), (45, 49), (50, 54), (55, 59)
        ]
        
        for min_rate, max_rate in rate_ranges:
            if min_rate <= commission_rate <= max_rate:
                return f"{min_rate}-{max_rate}"
                
        return None
        
    def clean_filename(self, filename):
        """清理文件名中的非法字符"""
        # 定义非法字符（Windows和Unix系统都不允许的字符）
        illegal_chars = '<>:"/\\|?*'
        # 替换非法字符为空格
        for char in illegal_chars:
            filename = filename.replace(char, ' ')
        # 去除首尾空格并限制长度
        filename = filename.strip()
        # 限制文件名长度，避免过长
        if len(filename) > 100:
            filename = filename[:100]
        return filename

    def safe_folder_name(self, name):
        """将任意名称转换为安全的文件夹名称"""
        if not name:
            return "未分类"
        # 复用clean规则，但允许更长一点
        cleaned = self.clean_filename(str(name))
        return cleaned or "未分类"
